package http

import (
	"context"
	"errors"
	"gitee.com/sdynasty/yogurt/internal/endpoint"
	"gitee.com/sdynasty/yogurt/internal/host"
	"gitee.com/sdynasty/yogurt/internal/matcher"
	"gitee.com/sdynasty/yogurt/log"
	"gitee.com/sdynasty/yogurt/middleware"
	"github.com/gin-gonic/gin"
	"net"
	"net/http"
	"net/url"
	"time"
)

const PayloadKey = "payload"

type ServerOption func(*Server)

type ContextWrapper interface {
	FromGinContext(*gin.Context) Context
}

// Network with server network.
func Network(network string) ServerOption {
	return func(s *Server) {
		s.network = network
	}
}

// Address with server address.
func Address(addr string) ServerOption {
	return func(s *Server) {
		s.address = addr
	}
}

func Timeout(timeout time.Duration) ServerOption {
	return func(s *Server) {
		s.timeout = timeout
	}
}

func Middleware(m ...middleware.Middleware) ServerOption {
	return func(s *Server) {
		s.middleware.Use(m...)
	}
}

func Listener(lis net.Listener) ServerOption {
	return func(s *Server) {
		s.lis = lis
	}
}

func EngineFitment(f func(engine *gin.Engine, ada GinAdapter)) ServerOption {
	return func(s *Server) {
		s.engineFitment = f
	}
}

type Server struct {
	*http.Server
	engine        *gin.Engine
	engineFitment func(engine *gin.Engine, ada GinAdapter)
	lis           net.Listener
	endpoint      *url.URL
	err           error
	network       string
	address       string
	timeout       time.Duration
	middleware    matcher.Matcher
	decVars       DecodeRequestFunc
	decQuery      DecodeRequestFunc
	decBody       DecodeRequestFunc
	enc           EncodeResponseFunc
	ene           EncodeErrorFunc
}

func NewServer(opts ...ServerOption) *Server {
	srv := &Server{
		network:    "tcp",
		address:    ":0",
		timeout:    1 * time.Second,
		middleware: matcher.New(),
		engine:     gin.New(),
		decBody:    DefaultRequestDecoder,
		enc:        DefaultResponseEncoder,
		ene:        DefaultErrorEncoder,
	}
	for _, o := range opts {
		o(srv)
	}
	g := srv.engine
	ada := newAdapter(srv)
	g.Use(ada.GinHandler())
	g.POST("", demoToolHandler(ada))
	if srv.engineFitment != nil {
		srv.engineFitment(g, ada)
	}
	return srv
}

func (s *Server) FromGinContext(g *gin.Context) Context {
	return nil
}

type data2 struct {
}

// 将ginHandler转换成通用Handler
func demoToolHandler(cw GinAdapter) gin.HandlerFunc {
	return func(c *gin.Context) {
		var ct Context = cw.FromGinContext(c)
		v := new(data2)
		if err := ct.Bind(v); err != nil {
			c.Error(err)
			return
		}

		h := ct.Middleware(func(ctx context.Context, req any) (any, error) {
			return demoBizFunc(ctx, req.(*data2))
		})
		out, err := h(ct, v)
		if err != nil {
			c.Error(err)
		}
		c.Set(PayloadKey, out)
	}
}
func demoBizFunc(ctx context.Context, req *data2) (*data2, error) {
	return nil, nil
}

func (s *Server) Start(ctx context.Context) error {
	if err := s.listenAndEndpoint(); err != nil {
		return err
	}
	log.Infof("[HTTP] server listening on: %s", s.lis.Addr().String())
	s.Server = &http.Server{Handler: s.engine.Handler()}
	err := s.Serve(s.lis)
	if !errors.Is(err, http.ErrServerClosed) {
		return err
	}
	return nil
}

func (s *Server) Stop(ctx context.Context) error {
	log.Info("[HTTP] server stopping")
	return s.Shutdown(ctx)
}

func (s *Server) listenAndEndpoint() error {
	if s.lis == nil {
		lis, err := net.Listen(s.network, s.address)
		if err != nil {
			s.err = err
			return err
		}
		s.lis = lis
	}
	if s.endpoint == nil {
		addr, err := host.Extract(s.address, s.lis)
		if err != nil {
			s.err = err
			return err
		}
		s.endpoint = endpoint.NewEndpoint(endpoint.Scheme("http", false), addr)
	}
	return nil
}
